Clojure で簡易的な構成管理ツールを作った話
TL;DR
タイトルの通り、作りました。
/icons/重要.icon 2019/12/10) 紆余曲折あり、名前を "dad" に変更しました。
経緯
新しいPCを購入したり、新しいVMを立ち上げた時のために chef, ansible などで個人用レシピを育てている方は少なからずいると思います。 かくいう私も、散らかっていてお恥ずかしいですが、cookbooks リポジトリで気が向いた時にレシピを育てていて、構成管理ツールとしてはシングルバイナリで新しい環境にインストールしやすい mitamae を使っています。 mitamae 自体に不満はほぼなく ruby でちょろっと書けるのは便利なのですが、いつの間にか ruby を書くのがレシピを書く時だけであることに気付いてしまいました。
ruby に触れる時間があまりに少なく、レシピを育てる時に「よし、えーと ruby はこう書くんだっけな」と重い腰を上げる形になってしまい、積極的にメンテナンスしようという気持ちも薄れてしまうことが多々あったので、それなら Clojure で書けるようにしたる!と思い作った次第です。
作ったもの
chef とか itamae とか料理人みたいな名前が多いですが、私の父こそが料理人であり、私にとって料理人 = 父なのです。
コマンドは dad です。
簡単な例として、誰しも1度はやるであろう vim のソースからのビルドは以下のような感じになります。 code:vim_source_build.clj
(def vars
{:src-dir "/usr/local/src/vim"
:configure (->> ["--prefix=/usr/local"
"--with-features=huge"
"--enable-multibyte"
"--enable-pythoninterp"
"--enable-python3interp"
"--enable-fail-if-missing"]
(str/join " "))
:packages ["build-essential"
"git"
"libncurses5-dev"
"python-dev"
"python3-dev"
"cmake"
"libboost-all-dev"
"sudo"]})
;; 既存の vim があれば削除
(package "vim" {:action :uninstall})
;; ビルドに必要なパッケージをインストール
(package packages)
;; ソースを git clone
;; configure して
(execute {:cwd src-dir
:command (str "./configure " configure)
:pre-not "test -e src/auto/config.log"})
;; インストール
(execute {:cwd src-dir
:command "make && make install"
:pre-not "test -e /usr/local/bin/vim"})
;; リビルド用の簡易スクリプトを用意
(template {:path (str src-dir "/rebuild.sh")
:source "rebuild.sh.tmpl"
:variables vars
:mode "755"}))
code:rebuild.sh.tmpl
git checkout .
git pull origin master
./configure {{configure}}
make
sudo make install
echo finished
実行は dad コマンドにレシピとなるファイルを渡すだけです。ね、簡単でしょう?
code:console
$ dad vim_source_build.clj
実装の話
評価結果は内部的には「task」というマップのリストに変換されて、あとはそのタスクをリソースにあるコマンド定義から該当するものを引っ張ってきて愚直に実行しているだけです。(runner.clj) 愚直にと言いつつ :requires やら :once? やら :pre / :pre-not といった定義のように無駄な処理をしない最低限の工夫はしてあり、意図しない挙動なども基本的には commands.edn だけ修正すれば(多分)直るような作りにしたつもりです。
また入力値のバリデーションには実験的に malli を使ってみています。 clojure.spec でもできるのですが、GraalVM でネイティブイメージ化する際に spec 関連は罠になりやすいので避けて、エラーメッセージの読みさすさにも気を使っている malli が良さそうという判断です。 今後
現状 ubuntu の apt と macOS の homebrew しか対応していないので、他の環境も対応してみたいなぁとは思いつつ、私の環境がその2つくらいなので気が向いたら対応します。
直近の目標は cookbooks リポジトリの mitamae レシピをすべて dad レシピに置き換えて、その中で見つかったバグを修正していけたらなぁと思っています。
最後に
まだかなりバギーな出来だとは思いますが、clj-xmas に次ぐ GraalVM を活用したツールが作れて満足です。